package com.baishui.gateway.path;

import com.alibaba.fastjson.JSON;
import com.baishui.gateway.exception.GateWayException;
import com.baishui.gateway.constant.Constants;
import com.baishui.gateway.path.vo.PathNode;
import com.baishui.gateway.vo.FrontInterfaceVo;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Data
public class PathDfaTree {
    private final static Logger log = LoggerFactory.getLogger(PathDfaTree.class);

    private static PathDfaTree pathDfaTree = new PathDfaTree();

    private static final Map<String, PathNode> firstPathNode = new ConcurrentHashMap<>();

    private PathDfaTree() {
    }

    public static PathDfaTree getInstance() {
        return pathDfaTree;
    }

    /**
     * buildPath
     *
     * @param frontInterfaceVoList frontInterfaceVoList
     * @throws GateWayException
     */
    public void buildPath(List<FrontInterfaceVo> frontInterfaceVoList) throws GateWayException {
        if (frontInterfaceVoList.isEmpty()) {
            throw new GateWayException("list is null when build path tree");
        }
        if (log.isDebugEnabled()) {
            log.debug("path size is {}", frontInterfaceVoList.size());
        }
        for (FrontInterfaceVo path : frontInterfaceVoList) {
            try {
                String[] pathArray = StringUtils.split(path.getPath(), Constants.SPRIT_SUFFIX);
                int depth = pathArray.length;
                int index = 1;
                if (depth > 0) {
                    PathNode currentNode;
                    currentNode = firstPathNode.get(pathArray[0]);
                    if (Objects.isNull(currentNode)) {
                        currentNode = new PathNode(pathArray[0]);
                        firstPathNode.put(pathArray[0], currentNode);
                    }
                    while (depth - 1 > 0) {
                        Map<String, PathNode> childrenNodes = currentNode.getChildrenNodes();
                        if (Objects.isNull(childrenNodes)) {
                            childrenNodes = new HashMap<>();
                        }
                        PathNode nextNode = childrenNodes.get(pathArray[index]);
                        if (Objects.isNull(nextNode)) {
                            nextNode = new PathNode(pathArray[index]);
                            childrenNodes.put(pathArray[index], nextNode);
                            currentNode.setChildrenNodes(childrenNodes);
                        }
                        currentNode = nextNode;
                        depth--;
                        index++;
                    }
                }
            } catch (Exception exception) {
                log.error("build path tree error", exception);
                throw new GateWayException("build path tree error", exception);
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("dfa tree is {}", JSON.toJSONString(firstPathNode));
        }

    }

    /**
     * getPathNode
     *
     * @param frontInterfaceVo frontInterfaceVo
     * @return PathNode
     * @throws GateWayException
     */
    public PathNode getPathNode(FrontInterfaceVo frontInterfaceVo) throws GateWayException {
        String path = frontInterfaceVo.getPath();
        if (StringUtils.isEmpty(path)) {
            throw new GateWayException("get pathNode error");
        }
        if (log.isDebugEnabled()) {
            log.debug("getPathNode path is {}", path);
        }
        try {
            String[] pathArray = StringUtils.split(path, Constants.SPRIT_SUFFIX);
            int depth = pathArray.length;
            int index = 1;
            if (depth > 0) {
                PathNode currentNode;
                currentNode = firstPathNode.get(pathArray[0]);
                while (depth - 1 > 0) {
                    Map<String, PathNode> childrenNodes = currentNode.getChildrenNodes();
                    currentNode = childrenNodes.get(pathArray[index]);
                    depth--;
                    index++;
                }
                if (log.isDebugEnabled()) {
                    log.debug("result patNode id is {}", currentNode.getPid());
                }
                return currentNode;
            }
        } catch (Exception exception) {
            log.error("get path tree error", exception);
            throw new GateWayException("get path tree error", exception);
        }
        return null;
    }

}
